package physic.engine;

import pong.game.objects.DinamicObject;
import pong.mathematic.Vec2;
import java.util.Vector;

public class MoveEngine// extends Thread
{
	private long 			timePassed 		= 0;
	private long 			curTime 		= 0;
	private long 			lastTime 		= 0;
	private float 			timeFraction 	= 0.0f;
	private float			widthWorld		= 0;
	private Vector<Vec2> 	constForces 	= new Vector<Vec2>();
	
	public static final float 			GRAVITY = 1.2f;
	public static final float 			DRAG 	= 0.0f;
	public static final float 			BOUNCE 	= 1.0f;
	public static Vector<DinamicObject>	GameObjects	= new Vector<DinamicObject>();
	
 	public MoveEngine(float widthWorld)
	{
 		this.widthWorld = widthWorld;
		curTime = System.currentTimeMillis();
		initializeConstForces();
	}
	
	public void run()
	{
		updateTime();
		applyConstForces();
		sumForces();
		moveEnts();
	}

	private void updateTime()
	{
		lastTime = curTime;
		curTime = System.currentTimeMillis();
		timePassed = (curTime - lastTime);
		timeFraction = ((float)timePassed / 1000.0f);
	}

	private void initializeConstForces()
	{
		//constForces.add(new Vec2(0.0, GRAVITY)); // GRAVITY
	}

	private synchronized void applyConstForces()
	{
		Vec2 accel = new Vec2();
		// Find the total acceleration of all const forces.
		for (int i = 0; i < constForces.size(); i++)
			accel.add(constForces.get(i));
		// Apply the sum acceleration to each entity.
		for (int i = 0; i < GameObjects.size(); i++) {
			DinamicObject s = GameObjects.get(i);
			s.addAccel(accel);
		}
	}

	private synchronized void sumForces()
	{
		for (int i = 0; i < GameObjects.size(); i++) {
			DinamicObject s = GameObjects.get(i);
			// Get the sum of all accelerations acting on object.
			Vec2 theAccel = s.sumAccel();
			// Apply the resulting change in velocity.
			Vec2 dir = new Vec2(s.dir.x + (theAccel.x * timeFraction), 
					s.dir.y + (theAccel.y * timeFraction));

			s.updateVelocity(dir);
			// Apply drag coefficient
			s.applyDrag(1.0f - (timeFraction * DRAG));
		}
	}

	private synchronized void moveEnts()
	{
		for (int i = 0; i < GameObjects.size(); i++) {
			DinamicObject s = GameObjects.get(i);
			// Calculate the new x and y coords.
			s.moveEnts(timeFraction);
			checkWallCollisions(s);
		}
		checkCollisions();
	}

	private synchronized void checkCollisions()
	{
		for (int i = 0; i < GameObjects.size() - 1; i++) {
			DinamicObject s = GameObjects.get(i);
			Vec2 sCenter = s.getCenter();
			for (int j = i + 1; j < GameObjects.size(); j++) {
				DinamicObject t = GameObjects.get(j);
				if (t == null) break;
				Vec2 tCenter = t.getCenter();
				float distBetween = sCenter.distance(tCenter);
				float bigR = s.radius > t.radius ? s.radius : t.radius;
				if (distBetween < (bigR * 2)) 
					collide(s, t, distBetween);
			}
		}
	}

	private synchronized void collide(DinamicObject s, DinamicObject t, float distBetween)
	{
		Vec2	sCenter = s.getCenter(),
				tCenter = t.getCenter();
		double relX = sCenter.x - tCenter.x;
		double relY = sCenter.y - tCenter.y;
		// Take the arctan to find the collision angle.
		double collisionAngle = Math.atan2(relY, relX);
		// if (collisionAngle < 0) collisionAngle += 2 * Math.PI;
		// Rotate the coordinate systems for each object's velocity to align
		// with the collision angle. We do this by supplying the collision angle
		// to the vector's rotateCoordinates method.
		Vector2D sVel = s.velVector(), tVel = t.velVector();
		sVel.rotateCoordinates(collisionAngle);
		tVel.rotateCoordinates(collisionAngle);
		// In the collision coordinate system, the contact normals lie on the
		// x-axis. Only the velocity values along this axis are affected. We can
		// now apply a simple 1D momentum equation where the new x-velocity of
		// the first object equals a negative times the x-velocity of the
		// second.
		double swap = sVel.x;
		sVel.x = tVel.x;
		tVel.x = swap;
		// Now we need to get the vectors back into normal coordinate space.
		sVel.restoreCoordinates();
		tVel.restoreCoordinates();
		// Give each object its new velocity.
		s.updateVelocity((float)(sVel.x * BOUNCE), (float)(sVel.y * BOUNCE));
		t.updateVelocity((float)(tVel.x * BOUNCE), (float)(tVel.y * BOUNCE));
		// Back them up in the opposite angle so they are not overlapping.
		double minDist = s.radius + t.radius;
		double overlap = minDist - distBetween;
		double toMove = overlap / 2;
		float newX = (float) (s.pos.x + (toMove * Math.cos(collisionAngle)));
		float newY = (float) (s.pos.y + (toMove * Math.sin(collisionAngle)));
		s.updatePos(newX, newY);
		newX = (float) (t.pos.x - (toMove * Math.cos(collisionAngle)));
		newY = (float) (t.pos.y - (toMove * Math.sin(collisionAngle)));
		t.updatePos(newX, newY);
	}

	private synchronized void checkWallCollisions(DinamicObject s)
	{
		float maxY = 1.0f - s.radius * 2;
		float maxX = widthWorld - s.radius * 2;
		
		if (s.pos.y > maxY) {
			s.updatePos(s.pos.x, maxY);
			s.updateVelocity(s.dir.x, (s.dir.y * -BOUNCE));
		}
		
		if (s.pos.y < 0) {
			s.updatePos(s.pos.x, 0);
			s.updateVelocity(s.dir.x, (s.dir.y * -BOUNCE));
		}
		
		if (s.pos.x > maxX) {
			s.updatePos(maxX, s.pos.y);
			s.updateVelocity((s.dir.x * -BOUNCE), s.dir.y);
		}
		if (s.pos.x < 0) {
			s.updatePos(0, s.pos.y);
			s.updateVelocity((s.dir.x * -BOUNCE), s.dir.y);
		}
	}
}
